home *** CD-ROM | disk | FTP | other *** search
- Path: news.th-darmstadt.de!news!enno
- From: enno@inferenzsysteme.informatik.th-darmstadt.de (Enno Sandner)
- Newsgroups: comp.lang.c++
- Subject: Re: Typecasting
- Date: 14 Jan 1996 10:19:55 GMT
- Organization: Fachbereich Informatik, TH Darmstadt
- Distribution: world
- Message-ID: <ENNO.96Jan14111955@kitz.inferenzsysteme.informatik.th-darmstadt.de>
- References: <30F763B0.319@dmr.ca>
- NNTP-Posting-Host: kitz.intellektik.informatik.th-darmstadt.de
- In-reply-to: Bernard Drolet's message of Fri, 12 Jan 1996 23:48:00 -0800
-
- In article <30F763B0.319@dmr.ca> Bernard Drolet <Bernard.Drolet@dmr.ca> writes:
-
- I'm writing a program using the Doc-View method.
-
- Presently, the main classes are (simplified)
-
- class Base
- {
- int GetType() = 0;
- // some other methods and variables
- };
-
- class A : public Base
- {
- int GetType() { return TypeA; }
- // methods and variables
- };
-
- class B : public Base
- {
- int GetType() { return TypeB; }
- // methods and variables
- };
-
- class Doc
- {
- List<Base> ListOfBaseObjects;
- Doc();
- };
-
- class View
- {
- Doc CurrentDocument;
- void Paint();
- void PaintAObject();
- void PaintBObject();
- };
-
- void Doc::Doc()
- {
- A* a = new A();
- B* b = new B();
-
- ListOfBaseObjects.Add(a);
- ListOfBaseObjects.Add(b);
- }
-
- void View::Paint()
- {
- Iterator<CurrentDocument.ListOfBaseObjects> ObjectsIterator;
-
- while ( ObjectsIterator )
- {
- switch ( ObjectsIterator.Current()->GetType() )
- {
- case TypeA:
- PaintAObject();
- break;
- case TypeB:
- PaintBObject();
- break;
- }
- ObjectsIterator++;
- }
- }
-
-
- What I would like better is to remove the switch statement in the paint() function and
- find a way to define functions looking like Paint(A& a), Paint(B& b) and to call them
- directly, but is there a way to discover what type an object that was inserted in the list
- is?
-
- I would like to replace the switch in the paint() with a statement like
- while ( ObjectsIterator )
- {
- Paint(ObjectsIterator.Current())
- ...
-
- So can I typecast the abstract Base object contained in the list to its actual type ?
-
- RTTI could help you to determine the dynamic-type of an instance. However
- this solution would lead to sth. similar to your solution (according to the
- drawbacks). A technique for adding type-specific behavior is the 'visitor'
- pattern. You have to augment the interface of your classes, add the 'Visitor'
- class and a subclass of 'Visitor' that implements the type-specific behavior.
- The 'Visitor' class contains a function for each possible type of your class
- hierarchie:
-
- class Visitor {
- public:
- virtual ~Visitor() {}
- virtual void VisitA(A&) {}
- virtual void VisitB(B&) {}
- };
-
- in this example the suitable subclass 'ViewVisitor' takes a reference to
- a 'View' and performs the appropriate operations.
-
- class ViewVisitor : public Visitor {
- public:
- ViewVisitor(View& view) : view_(view) {}
- void VisitA(A&) { view_.PaintAObject(); }
- void VisitB(B&) { view_.PaintBObject(); }
- private:
- View& view_;
- };
-
- For other tasks, you simply create a new subclass.
- Now the interface of 'Base' is enriched with the 'void Accept(Visitor&)'
- function, which is implemented in each subclass by simply calling back
- the 'Visitor' with the appropriate type, ie:
-
- void A :: Accept(Visitor& v) { v.VisitA(*this); }
- void B :: Accept(Visitor& v) { v.VisitB(*this); }
-
- The resulting 'View :: paint' writes as:
-
- void View::Paint()
- {
- Iterator<CurrentDocument.ListOfBaseObjects> ObjectsIterator;
- ViewVisitor vview(*this);
-
- while ( ObjectsIterator )
- {
- ObjectsIterator.Current()->Accept(vview);
- ObjectsIterator++;
- }
- }
-
- This solution makes it easy to 'plug-in' new behavior, but adding new classes
- enforce a modification of the 'Visitor' interface.
-
- Enno
-